package com.hp.es.elasticsearch.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hp.es.elasticsearch.entry.DocBean;
import com.hp.es.elasticsearch.service.IElasticService;
import com.hp.es.elasticsearch.util.ObjectToMapUtils;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

/**
 * @Author hp
 * @Title es 测试实现类
 * @Package
 * @Date 2021/9/28 17:28
 * @Description
 * @Version 1.0
 */
@Slf4j
@Service
public class ElasticServiceImpl implements IElasticService {
    @Resource
    private RestHighLevelClient restHighLevelClient;
    private static final String NBA_INDEX = "record_traffic-2020-12-10";
    @Override
    public void save(DocBean docBean) throws IOException {
        IndexRequest indexRequest = new IndexRequest(NBA_INDEX).id(String.valueOf(docBean.getId())).source(ObjectToMapUtils.beanToMap(docBean));
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        log.info("执行结果json字符串:{}",JSONObject.toJSON(indexResponse));
    }

    @Override
    public void batchSave(List<DocBean> docBeanList) throws IOException {
        if(CollectionUtils.isEmpty(docBeanList)){
            throw new IllegalArgumentException("docBeanList is not null");
        }
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout(new TimeValue(60, TimeUnit.SECONDS));
        docBeanList.stream().forEach(s -> {
            bulkRequest.add(new IndexRequest(NBA_INDEX).id(String.valueOf(s.getId())).source(ObjectToMapUtils.beanToMap(s)));
        });
        // 执行请求
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest,RequestOptions.DEFAULT);
        // 响应 判断是否执行成功
        RestStatus status = bulkResponse.status();
        if(status.name().equals("OK")){
            log.info("批量添加返回状态：{}",status);
        }else{
            log.info("批量添加返回状态：{}",status);
        }
    }

    @Override
    public void update(DocBean docBean) throws IOException {
        UpdateRequest updateRequest = new UpdateRequest(NBA_INDEX,docBean.getId()).doc(ObjectToMapUtils.beanToMap(docBean));
        UpdateResponse updateResponse = restHighLevelClient.update(updateRequest,RequestOptions.DEFAULT);
        RestStatus status = updateResponse.status();
        if(status.name().equals("OK")){
            log.info("更新数据返回状态：{}",status);
        }else{
            log.info("更新数据返回状态：{}",status);
        }
    }

    @Override
    public void deleteById(String id) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(NBA_INDEX,id);
        DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT);
        RestStatus status = deleteResponse.status();
        if(status.name().equals("OK")){
            log.info("删除数据返回状态：{}",status);
        }else{
            log.info("删除数据返回状态：{}",status);
        }
    }

    @Override
    public void deleteByIds(String[] ids) throws IOException {
        DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(NBA_INDEX);
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.idsQuery().addIds(ids));
        deleteByQueryRequest.setQuery(boolQueryBuilder);
        restHighLevelClient.deleteByQuery(deleteByQueryRequest,RequestOptions.DEFAULT);
    }

    @Override
    public void getById(String id) throws IOException {
        GetRequest getRequest = new GetRequest(NBA_INDEX,id);
        GetResponse getResponse = restHighLevelClient.get(getRequest,RequestOptions.DEFAULT);
        log.info("查询结果 {}",JSONObject.toJSON(getResponse.getSource()));
    }

    @Override
    public Map<String,Long> getTypeGroupBy() throws IOException {
        Map<String,Long> map = new LinkedHashMap<>();
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("record_traffic*");
        //指定分组字段,terms指定别名,field指定字段名
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders
                //别名
                .terms("content")
                //聚合字段名
                .field("content.keyword")
                .size(100)
                // 降序
                .order(BucketOrder.count(false));
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(aggregationBuilder);
        //执行查询
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        RestStatus status = searchResponse.status();
        if(status.name().equals("OK")){
            Terms byAgeAggregation = searchResponse.getAggregations().get("content");
            List<? extends Terms.Bucket> buckets = byAgeAggregation.getBuckets();
            for(Terms.Bucket bucket : buckets){
                map.put(bucket.getKeyAsString(),bucket.getDocCount());
            }
        }else{
            throw new IllegalArgumentException("ERROR");
        }

        return map;
    }

    @Override
    public boolean createIndex(String index) throws IOException {
        //1.创建索引请求
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);
        //2.客户端执行请求IndicesClient,请求后获得相应
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest,RequestOptions.DEFAULT);
        return createIndexResponse.isAcknowledged();
    }

    @Override
    public boolean isExitIndex(String index) throws IOException {
        GetIndexRequest getIndexRequest = new GetIndexRequest(index);
        boolean exists = restHighLevelClient.indices().exists(getIndexRequest,RequestOptions.DEFAULT);
        return exists;
    }

    @Override
    public boolean deleteIndex(String index) throws IOException {
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index);
        AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(deleteIndexRequest,RequestOptions.DEFAULT);
        return acknowledgedResponse.isAcknowledged();
    }

    @Override
    public boolean addDocument(String index, String id, Object o) throws IOException {
        IndexRequest indexRequest = new IndexRequest(index);
        //规则 一般的文档形如 put /index/_doc/1
        indexRequest.id(id);//如果不设置id的话会自动分配id
        indexRequest.timeout("1s");//设置超时时间
        //将我们的数据放入请求Json中
        indexRequest.source(JSON.toJSONString(o), XContentType.JSON);
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);

        return indexResponse.getShardInfo().getSuccessful() > 0 ? true : false;
    }

    @Override
    public boolean documentIsExit(String index, String id) throws IOException {
        GetRequest getRequest = new GetRequest(index,id);
        //不获取返回的_source上下文
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");

        return restHighLevelClient.exists(getRequest,RequestOptions.DEFAULT);
    }

    @Override
    public String getDoucumment(String index, String id) throws IOException {
        GetRequest getRequest = new GetRequest(index,id);
        GetResponse getResponse = restHighLevelClient.get(getRequest,RequestOptions.DEFAULT);

        return getResponse.getSourceAsString();
    }

    @Override
    public boolean updateDocument(String index, String id, Object o) throws IOException {
        UpdateRequest updateRequest = new UpdateRequest(index,id);
        updateRequest.timeout("1s");
        updateRequest.doc(JSON.toJSONString(o),XContentType.JSON);
        UpdateResponse updateResponse = restHighLevelClient.update(updateRequest,RequestOptions.DEFAULT);

        return updateResponse.getShardInfo().getSuccessful() > 0 ? true : false;
    }

    @Override
    public boolean deleteDocument(String index, String id) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(index,id);
        deleteRequest.timeout("1s");
        DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT);

        return deleteResponse.getShardInfo().getSuccessful() > 0 ? true : false;
    }

    @Override
    public boolean batchAddDocument(String index, String id, ArrayList<Object> list) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("1s");
        //批量处理请求
        list.stream().forEach(s -> {
            bulkRequest.add(new IndexRequest(index).id(id).source(JSON.toJSONString(s),XContentType.JSON));
        });
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest,RequestOptions.DEFAULT);
        //是否失败 false-没有失败
        return bulkResponse.hasFailures() ? false : true;
    }

    @Override
    public List<Map<String, Object>> termQuery(String index, TreeMap<String, Object> content, int size, int from, boolean ishigh) throws IOException {
        SearchRequest searchRequest = new SearchRequest(index);
        //构建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        log.info("content.firstKey = {}",content.firstKey());
        //查询条件
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery(content.firstKey(),content.get(content.firstKey()));
        searchSourceBuilder.query(termQueryBuilder);
        searchSourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
        //获取多少条数据
        searchSourceBuilder.size(size);
        //从第几行开始
        searchSourceBuilder.from(from);
        //是否要将查询的结果中将搜索的关键词高亮
        if(ishigh){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            //设置高亮的属性
            highlightBuilder.field(content.firstKey());
            //也可以自定义高亮的样式,这里我使用的是默认的方式
            searchSourceBuilder.highlighter(highlightBuilder);
        }
        //将查询条件放入需要查询中
        searchRequest.source(searchSourceBuilder);
        //获取相应的数据
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        SearchHit[] searchHit = searchResponse.getHits().getHits();
        ArrayList<Map<String,Object>> result = new ArrayList<>();
        Arrays.stream(searchHit).forEach(s -> {
            Map<String, HighlightField> highlightFieldMap = s.getHighlightFields();
            //获取高亮的信息
            HighlightField property = highlightFieldMap.get(content.firstKey());
            //查询的元素数据(没有高亮)
            Map<String,Object> stringObjectMap = s.getSourceAsMap();
            if(ishigh){
                if(property != null){
                    Text[] fragments = property.fragments();
                    String n_title = "";
                    for(Text t : fragments){
                        n_title += t;
                    }
                    stringObjectMap.put(content.firstKey(),n_title);
                }
            }
            result.add(stringObjectMap);
        });

        return result;
    }

    @Override
    public List<Map<String, Object>> matchQuery(String index, TreeMap<String, Object> content, int size, int from, boolean ishigh) throws IOException {
        SearchRequest searchRequest = new SearchRequest(index);
        //构建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        log.info("content.firstKey = {}",content.firstKey());
        //查询条件
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(content.firstKey(),content.get(content.firstKey()));
        searchSourceBuilder.query(matchQueryBuilder);
        searchSourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
        //获取多少条数据
        searchSourceBuilder.size(size);
        //从第几行开始
        searchSourceBuilder.from(from);
        //是否要将查询的结果中将搜索的关键词高亮
        if(ishigh){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            //设置高亮的属性
            highlightBuilder.field(content.firstKey());
            //也可以自定义高亮的样式,这里我使用的是默认的方式
            searchSourceBuilder.highlighter(highlightBuilder);
        }
        //将查询条件放入需要查询中
        searchRequest.source(searchSourceBuilder);
        //获取相应的数据
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        SearchHit[] searchHit = searchResponse.getHits().getHits();
        ArrayList<Map<String,Object>> result = new ArrayList<>();
        Arrays.stream(searchHit).forEach(s -> {
            Map<String, HighlightField> highlightFieldMap = s.getHighlightFields();
            //获取高亮的信息
            HighlightField property = highlightFieldMap.get(content.firstKey());
            //查询的元素数据(没有高亮)
            Map<String,Object> stringObjectMap = s.getSourceAsMap();
            if(ishigh){
                if(property != null){
                    Text[] fragments = property.fragments();
                    String n_title = "";
                    for(Text t : fragments){
                        n_title += t;
                    }
                    stringObjectMap.put(content.firstKey(),n_title);
                }
            }
            result.add(stringObjectMap);
        });

        return result;
    }

    @Override
    public List<Map<String, Object>> boolmustQuery(String index, TreeMap<String, Object> content, int size, int from) throws IOException {
        SearchRequest searchRequest = new SearchRequest(index);
        //构建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        Set keys = content.keySet();
        for(Object key : keys){
            //将要查询的条件加入
            boolQueryBuilder.must(QueryBuilders.termQuery((String) key,content.get(key)));
        }
        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
        //获取多少条数据
        searchSourceBuilder.size(size);
        //从第几行开始
        searchSourceBuilder.from(from);
        //将查询条件放入需要查询中
        searchRequest.source(searchSourceBuilder);
        //获取相应的数据
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        ArrayList<Map<String, Object>> result = new ArrayList<>();
        Arrays.stream(searchHits).forEach(s -> {
            //Map<String, HighlightField> highlightFieldMap = s.getHighlightFields();
            result.add(s.getSourceAsMap());
        });

        return result;
    }
}
